package com.jnc.pay.biz.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpRequest;
import com.jnc.pay.biz.common.model.WechatpayConfig;
import com.jnc.pay.biz.common.service.PaymentService;
import com.jnc.pay.biz.common.vo.*;
import com.jnc.pay.constant.BizConstant;
import com.jnc.pay.constant.SysConstant;
import com.jnc.pay.constant.WechatPayConstant;
import com.jnc.pay.core.exception.BusinessException;
import com.jnc.pay.core.model.BaseResp;
import com.jnc.pay.core.model.RespCode;
import com.jnc.pay.util.convert.ConvertUtil;
import com.jnc.pay.util.convert.CustomBeanUtil;
import com.jnc.pay.util.gen.IdGen;
import com.jnc.pay.util.pay.SignUtil;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.*;
import java.io.*;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Map;

/**
 * @Auther: jjn
 * @Date: 2020/6/24 17:40
 * @Desc:
 */
@Slf4j
@SuppressWarnings("unchecked")
public abstract class AbstractWechatPay implements PaymentService {

    private int timeout = 5000;

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    protected abstract void addTrade(PayReq req);

    protected abstract void addRefund(RefundReq req);

    protected abstract void addContract(ContractReq req);

    /**
     * 企业付款到零钱
     * @param req
     * @return
     */
    public abstract BaseResp<Map<String, Object>> doEnterPayChange(PayChangeReq req);

    /**
     * 签约
     * @param req
     * @return
     */
    public abstract BaseResp<Map<String, Object>> doContract(ContractReq req);

    /**
     * post请求封装方法
     * @param url
     * @param xml
     * @return
     */
    protected BaseResp<Map<String, Object>> postAb(String url, String xml){
        log.debug("Send request to wechat, url: {}, req: {}", url, xml);
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        try{
            String result = HttpRequest.post(url)
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .timeout(timeout)
                    .body(xml)
                    .execute().body();
            log.debug("Send request to wechat response info, url: {}, xml: {}, resp: {}", url, xml, XmlUtil.format(result));
            Map<String, Object> map = XmlUtil.xmlToMap(result);
            String return_code = MapUtil.getStr(map, "return_code");
            String return_msg = MapUtil.getStr(map, "return_msg");
            String result_code = MapUtil.getStr(map, "result_code");
            //成功
            if(WechatPayConstant.SUCCESS_CODE.equals(return_code)){
                if(WechatPayConstant.SUCCESS_CODE.equals(result_code)){
                    resp.setCode(RespCode.SUCCESS.getCode())
                            .setMsg(RespCode.SUCCESS.getDesc())
                            .setData(map);
                }else{
                    String err_code = MapUtil.getStr(map, "err_code");
                    log.warn("Send request to wechat failed, url: {}, req: {}, fail msg: {}", url, xml, return_msg);
                    resp.setCode(RespCode.WX_BIZ_ERROR.getCode()).setMsg(StrUtil.isBlank(err_code) ? "Bad Request" : err_code).setData(map);
                }
            }else{
                log.warn("Send request to wechat failed, url: {}, req: {}, fail msg: {}", url, xml, return_msg);
                resp.setCode(RespCode.WX_BIZ_ERROR.getCode()).setMsg("Bad Request");
            }
            return resp;
        }catch (Exception e){
            log.error("Send request to wechat error, url: {}, req: {}, error: {}", url, xml, e);
            return resp.setCode(RespCode.WX_SYS_ERROR.getCode()).setMsg("Internal Server Error");
        }
    }

    /**
     * ssl post请求封装类
     * @param url
     * @param xml
     * @param wechatpayConfig
     * @return
     */
    protected BaseResp<Map<String, Object>> postAbSSL(String url, String xml, WechatpayConfig wechatpayConfig){
        log.debug("Send ssl request to wechat, url: {}, req: {}", url, xml);
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        try{
            String result = HttpRequest.post(url).setSSLSocketFactory(getSSLSocketFactory(wechatpayConfig))
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .timeout(timeout)
                    .body(xml)
                    .execute().body();
            log.debug("Send ssl request to wechat response info, url: {}, xml: {}, resp: {}", url, xml, XmlUtil.format(result));
            Map<String, Object> map = XmlUtil.xmlToMap(result);
            String return_code = MapUtil.getStr(map, "return_code");
            String return_msg = MapUtil.getStr(map, "return_msg");
            String result_code = MapUtil.getStr(map, "result_code");
            //成功
            if(WechatPayConstant.SUCCESS_CODE.equals(return_code)){
                if(WechatPayConstant.SUCCESS_CODE.equals(result_code)){
                    resp.setCode(RespCode.SUCCESS.getCode())
                            .setMsg(RespCode.SUCCESS.getDesc())
                            .setData(map);
                }else{
                    String err_code = MapUtil.getStr(map, "err_code");
                    log.warn("Send ssl request to wechat failed, url: {}, req: {}, fail msg: {}", url, xml, return_msg);
                    resp.setCode(RespCode.WX_BIZ_ERROR.getCode()).setMsg(StrUtil.isBlank(err_code) ? "Bad Request" : err_code).setData(map);
                }
            }else{
                log.warn("Send ssl request to wechat failed, url: {}, req: {}, fail msg: {}", url, xml, return_msg);
                resp.setCode(RespCode.WX_BIZ_ERROR.getCode()).setMsg("Bad Request");
            }
            return resp;
        }catch (Exception e){
            log.error("Send request to wechat error, url: {}, req: {}, error: {}", url, xml, e);
            return resp.setCode(RespCode.WX_SYS_ERROR.getCode()).setMsg("Internal Server Error");
        }
    }

    /**
     * 封装微信支付参数
     * @param map
     * @param req
     * @return
     */
    protected String fillPayReq(Map<String, Object> map, PayReq req){
        //2、组装微信支付参数
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("sign_type", BizConstant.SIGN_TYPE_MD5);  //签名算法类型（MD5）
        map.put("body", req.getBody());  //商品描述（显示到拉起的支付窗口标题）
        map.put("out_trade_no", ConvertUtil.toStr(req.getTradeId()));     //商户订单号（商户号下唯一）
        map.put("fee_type", req.getCurrencyCode());  //币种
        map.put("total_fee", req.getTotalFee());     //订单总金额，单位为分
        map.put("spbill_create_ip", req.getClientIp());  //用户的客户端IP
        map.put("notify_url", req.getPlatformNotifyUrl());    //平台通知URL
        map.put("trade_type", req.getPayType());      //支付类型
        map.put("detail", req.getDetail());    //商品详细信息
        if(StrUtil.isNotBlank(req.getChannelUserId())){
            map.put("openid", req.getChannelUserId());   //openid
        }
        if(StrUtil.isNotBlank(req.getSceneInfo())){ //H5 场景信息
            map.put("scene_info", req.getSceneInfo());
        }
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信支付响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillPayResp(String xmlReq, PayReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        switch (req.getPayType()){
            case WechatPayConstant.PAY_TYPE_NATIVE:
                respMap.put("trade_id", req.getTradeId());
                respMap.put("app_order_id", req.getAppOrderId());
                respMap.put("out_app_id", req.getAppId());
                BaseResp<Map<String, Object>> resp = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_PAY, xmlReq);
                if(resp.getCode() == RespCode.SUCCESS.getCode()){
                    //保存支付订单信息
                    addTrade(req);
                    Map<String, Object> data = resp.getData();
                    respMap.put("code_url", MapUtil.getStr(data, "code_url"));
                    respMap.put("prepay_id", MapUtil.getStr(data, "prepay_id"));
                    respMap.put("nonce_str", MapUtil.getStr(data, "nonce_str"));
                    resp.setData(respMap);
                }
                return resp;
            case WechatPayConstant.PAY_TYPE_H5:
                respMap.put("trade_id", req.getTradeId());
                respMap.put("app_order_id", req.getAppOrderId());
                respMap.put("out_app_id", req.getAppId());
                respMap.put("return_url", req.getReturnUrl());
                BaseResp<Map<String, Object>> respH5 = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_PAY, xmlReq);
                if(respH5.getCode() == RespCode.SUCCESS.getCode()){
                    //保存支付订单信息
                    addTrade(req);
                    Map<String, Object> data = respH5.getData();
                    respMap.put("mweb_url", MapUtil.getStr(data, "mweb_url"));
                    respMap.put("prepay_id", MapUtil.getStr(data, "prepay_id"));
                    respMap.put("nonce_str", MapUtil.getStr(data, "nonce_str"));
                    respH5.setData(respMap);
                }
                return respH5;
            default:
                respMap.put("trade_id", req.getTradeId());
                respMap.put("app_order_id", req.getAppOrderId());
                respMap.put("out_app_id", req.getAppId());
                respMap.put("appid", req.getWechatpayConfig().getWechatpayAppId());
                respMap.put("sign_type", BizConstant.SIGN_TYPE_MD5);
                BaseResp<Map<String, Object>> respDefault = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_PAY, xmlReq);
                if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
                    //保存支付订单信息
                    addTrade(req);
                    Map<String, Object> data = respDefault.getData();
                    respMap.put("prepay_id", MapUtil.getStr(data, "prepay_id"));
                    respMap.put("nonce_str", MapUtil.getStr(data, "nonce_str"));
                    respMap.put("timestamp", Instant.now().getEpochSecond());
                    String respSign = SignUtil.getSign(respMap, req.getWechatpayConfig().getApiSecret());
                    respMap.put("sign", respSign);
                    respDefault.setData(respMap);
                }
                return respDefault;
        }
    }

    /**
     * 封装微信支付状态查询请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillQueryReq(Map<String, Object> map, QueryReq req){
        //2、组装微信支付状态查询参数
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("sign_type", BizConstant.SIGN_TYPE_MD5);  //签名算法类型（MD5）
        map.put("out_trade_no", ConvertUtil.toStr(req.getTradeId()));     //商户订单号（商户号下唯一）
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信支付状态查询响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillQueryResp(String xmlReq, QueryReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("trade_id", req.getTradeId());
        respMap.put("app_order_id", req.getAppOrderId());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_QUERY, xmlReq);
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            Map<String, Object> data = respDefault.getData();
            respMap.put("trade_state", MapUtil.getStr(data, "trade_state"));
            respMap.put("trade_state_desc", MapUtil.getStr(data, "trade_state_desc"));
            respMap.put("time_end", MapUtil.getStr(data, "time_end"));
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 组装退款请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillRefundReq(Map<String, Object> map, RefundReq req){
        //2、组装微信退款参数
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("sign_type", BizConstant.SIGN_TYPE_MD5);  //签名算法类型（MD5）
        map.put("out_trade_no", ConvertUtil.toStr(req.getTradeId()));     //商户订单号（商户号下唯一）
        map.put("transaction_id", req.getTradeNo());   //第三方流水号
        map.put("out_refund_no", req.getRefundId());   //退款ID
        map.put("refund_fee_type", req.getCurrencyCode());  //币种
        map.put("total_fee", req.getTotalFee());     //订单总金额，单位为分
        map.put("refund_fee", req.getRefundFee());   //退款金额
        map.put("refund_desc", req.getRefundReason());  //退款原因
        map.put("notify_url", req.getPlatformNotifyUrl());    //通知URL
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);
        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 组装退款响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillRefundResp(String xmlReq, RefundReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("trade_id", req.getTradeId());
        respMap.put("refund_id", req.getRefundId());
        respMap.put("app_order_id", req.getAppOrderId());
        respMap.put("app_refund_id", req.getAppRefundId());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAbSSL(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_REFUND, xmlReq, req.getWechatpayConfig());
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            addRefund(req);
            Map<String, Object> data = respDefault.getData();
            respMap.put("refund_fee", MapUtil.getStr(data, "refund_fee"));
            respMap.put("nonce_str", MapUtil.getStr(data, "nonce_str"));
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 封装微信退款查询请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillRefundQueryReq(Map<String, Object> map, RefundQueryReq req){
        //2、组装微信支付参数
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("sign_type", BizConstant.SIGN_TYPE_MD5);  //签名算法类型（MD5）
        map.put("out_refund_no", ConvertUtil.toStr(req.getRefundId()));     //商户退款单号（商户号下唯一）
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信退款查询响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillRefundQueryResp(String xmlReq, RefundQueryReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("refund_id", req.getRefundId());
        respMap.put("app_refund_id", req.getAppRefundId());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_REFUND_QUERY, xmlReq);
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            Map<String, Object> data = respDefault.getData();
            respMap.put("refund_fee", MapUtil.getInt(data, "refund_fee_$0"));
            respMap.put("refund_status", MapUtil.getStr(data, "refund_status_$0"));
            respMap.put("refund_success_time", MapUtil.getStr(data, "refund_success_time_$0"));
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 封装微信企业付款到零钱请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillPayChangeReq(Map<String, Object> map, PayChangeReq req){
        //2、组装微信企业付款到零钱参数
        map.put("mch_appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mchid", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("partner_trade_no", ConvertUtil.toStr(req.getTransId()));     //商户订单号（商户号下唯一）
        map.put("openid", req.getChannelUserId());   //openid
        map.put("check_name", "NO_CHECK");   //校验用户姓名选项
        map.put("amount", req.getTotalFee());     //金额，单位为分
        map.put("desc", req.getTransDesc());   //企业付款备注
        map.put("spbill_create_ip", req.getClientIp());    //客户端IP
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);
        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信签约请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillContractReq(Map<String, Object> map, ContractReq req) {
        //2、组装微信支付参数
        long serial = IdGen.getId();
        String timestamp = ConvertUtil.toStr(System.currentTimeMillis() / 1000);
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("plan_id", req.getPlanId());  //模板id
        map.put("contract_code", req.getContractCode());  //签约协议号
        map.put("request_serial", serial);   //请求序列号
        map.put("contract_display_account", req.getContractName());  //用户账户展示名称
        map.put("notify_url", req.getPlatformNotifyUrl()); //平台回调接口url
        map.put("version", WechatPayConstant.CONTRACT_VERSION); //版本号
        map.put("timestamp", timestamp); //时间戳
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        if(StrUtil.equals(req.getPayType(), WechatPayConstant.CONTRACT_TYPE_JSAPI)){
            return contractJsapiReq(req, timestamp, sign, serial);
        }else if(StrUtil.equals(req.getPayType(), WechatPayConstant.CONTRACT_TYPE_APP)){
            return XmlUtil.mapToXmlStr(map);
        }
        throw new BusinessException("payType not supported for contract");
    }

    /**
     * 封装微信APP签约响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillContractResp(String xmlReq, ContractReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("contract_id", req.getContractId());
        respMap.put("contract_code", req.getContractCode());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_CONTRACT_APP, xmlReq);
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            addContract(req);
            Map<String, Object> data = respDefault.getData();
            respMap.put("appid", MapUtil.getStr(data, "appid"));
            respMap.put("sign", MapUtil.getStr(data, "sign"));
            respMap.put("nonce_str", MapUtil.getStr(data, "nonce_str"));
            respMap.put("pre_entrustweb_id", MapUtil.getStr(data, "pre_entrustweb_id"));
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 封装微信签约查询请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillContractQueryReq(Map<String, Object> map, ContractQueryReq req) {
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("plan_id", req.getPlanId());  //模板id
        map.put("contract_code", req.getContractCode());  //签约协议号
        if(StrUtil.isNotBlank(req.getContractNo())){
            map.put("contract_id", req.getContractNo());  //委托代扣协议id
        }
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信签约查询响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillContractQueryResp(String xmlReq, ContractQueryReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("contract_id", req.getContractNo());
        respMap.put("contract_code", req.getContractCode());
        respMap.put("plan_id", req.getPlanId());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_CONTRACT_QUERY, xmlReq);
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            Map<String, Object> data = respDefault.getData();
            respMap.put("contract_state", MapUtil.getInt(data, "contract_state"));
            respMap.put("contract_signed_time", MapUtil.getStr(data, "contract_signed_time"));
            respMap.put("contract_expired_time", MapUtil.getStr(data, "contract_expired_time"));
            respMap.put("contract_terminated_time", MapUtil.getStr(data, "contract_terminated_time"));
            respMap.put("contract_termination_mode", MapUtil.getInt(data, "contract_termination_mode"));
            respMap.put("contract_termination_remark", MapUtil.getStr(data, "contract_termination_remark"));
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 封装微信解约请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillTerminationReq(Map<String, Object> map, TerminationReq req) {
        //2、组装微信支付参数
        long serial = IdGen.getId();
        String timestamp = ConvertUtil.toStr(System.currentTimeMillis() / 1000);
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("plan_id", req.getPlanId());  //模板id
        map.put("contract_code", req.getContractCode());  //签约协议号
        if(StrUtil.isNotBlank(req.getContractNo())){
            map.put("contract_id", req.getContractNo());  //委托代扣协议id
        }
        map.put("contract_termination_remark", req.getTerminationDesc());  //解约原因
        map.put("version", WechatPayConstant.CONTRACT_VERSION); //版本号
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信解约响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillTerminationResp(String xmlReq, TerminationReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("contract_no", req.getContractNo());
        respMap.put("contract_code", req.getContractCode());
        respMap.put("plan_id", req.getPlanId());
        respMap.put("out_app_id", req.getAppId());
        BaseResp<Map<String, Object>> respDefault = postAbSSL(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_TERMINATION, xmlReq, req.getWechatpayConfig());
        if(respDefault.getCode() == RespCode.SUCCESS.getCode()){
            respDefault.setData(respMap);
        }
        return respDefault;
    }

    /**
     * 封装微信代扣请求参数
     * @param map
     * @param req
     * @return
     */
    protected String fillWithholdReq(Map<String, Object> map, WithholdReq req){
        //2、组装微信支付参数
        map.put("appid", req.getWechatpayConfig().getWechatpayAppId());  //微信支付分配的公众账号ID
        map.put("mch_id", req.getWechatpayConfig().getMerchantId());     //微信支付分配的商户号
        map.put("nonce_str", CustomBeanUtil.getRandomStr(32).toUpperCase());  //随机字符串
        map.put("body", req.getBody());  //商品描述（显示到拉起的支付窗口标题）
        map.put("out_trade_no", ConvertUtil.toStr(req.getTradeId()));     //商户订单号（商户号下唯一）
        map.put("fee_type", req.getCurrencyCode());  //币种
        map.put("total_fee", req.getTotalFee());     //订单总金额，单位为分
        map.put("spbill_create_ip", req.getClientIp());  //用户的客户端IP
        map.put("notify_url", req.getPlatformNotifyUrl());    //平台通知URL
        map.put("trade_type", req.getPayType());      //支付类型
        map.put("detail", req.getDetail());    //商品详细信息
        map.put("contract_id", req.getContractNo()); //委托代扣协议id
        String sign = SignUtil.getSign(map, req.getWechatpayConfig().getApiSecret());
        map.put("sign", sign);

        return XmlUtil.mapToXmlStr(map);
    }

    /**
     * 封装微信代扣响应参数
     * @param xmlReq
     * @param req
     * @return
     */
    protected BaseResp<Map<String, Object>> fillWithholdResp(String xmlReq, WithholdReq req){
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("trade_id", req.getTradeId());
        respMap.put("app_order_id", req.getAppOrderId());
        respMap.put("out_app_id", req.getAppId());
        respMap.put("contract_no", req.getContractNo());
        BaseResp<Map<String, Object>> resp = postAb(WechatPayConstant.WECHAT_GATEWAY + WechatPayConstant.WECHAT_WITHHOLD, xmlReq);
        if(resp.getCode() == RespCode.SUCCESS.getCode()){
            //保存代扣订单信息
            PayReq pay = new PayReq();
            BeanUtil.copyProperties(req, pay);
            addTrade(pay);
            resp.setData(respMap);
        }
        return resp;
    }

    /**
     * 生成SSLSocketFactory
     * @param wechatpayConfig
     * @return
     * @throws Exception
     */
    private SSLSocketFactory getSSLSocketFactory(WechatpayConfig wechatpayConfig) throws Exception {
        //导入客户端证书
        KeyStore ks=KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(wechatpayConfig.getApiCertificate()));
        //FileInputStream instream = new FileInputStream(new File("D:\\cert\\apiclient_cert.p12"));
        ks.load(instream, wechatpayConfig.getMerchantId().toCharArray());
        KeyManagerFactory kmf= KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, wechatpayConfig.getMerchantId().toCharArray());
        //添加信任证书
        TrustManager[] tm={new AllTrustManager()};//AllTrustManager()为信任所有证书

        SSLContext ctx=SSLContext.getInstance("SSL");//创建ssl上下文
        //初始化 ；参数1为null，则不上传客户端证书（通常情况都是如此）；
        ctx.init(kmf.getKeyManagers(), tm, new SecureRandom());
        //ctx.init(kmf.getKeyManagers(), null, new SecureRandom());//验证系统默认证书
        //ctx.init(kmf.getKeyManagers(), TrustManager[] tm, new SecureRandom());//导出服务端证书，然后按照keymanager一样实现trustmanager
        return ctx.getSocketFactory();
    }

    /**
     * 用于信任所有证书
     */
    class AllTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    /**
     * 封装公众号签约请求参数
     * @param req
     * @param timestamp
     * @param sign
     * @param serial
     * @return
     */
    private String contractJsapiReq(ContractReq req, String timestamp, String sign, long serial){
        String encode = "";
        try {
            encode = URLEncoder.encode(req.getPlatformNotifyUrl(), SysConstant.CHARSET_NAME);
        } catch (UnsupportedEncodingException e) {
            log.error("encode notifyUrl error: {}", e);
            e.printStackTrace();
        }
        StringBuffer sb = new StringBuffer();
        sb.append(WechatPayConstant.WECHAT_GATEWAY).append(WechatPayConstant.WECHAT_CONTRACT_JSAPI)
                .append("?appid=").append(req.getWechatpayConfig().getWechatpayAppId())
                .append("&mch_id=").append(req.getWechatpayConfig().getMerchantId())
                .append("&plan_id=").append(req.getPlanId())
                .append("&contract_code=").append(req.getContractCode())
                .append("&request_serial=").append(serial)
                .append("&contract_display_account=").append(req.getContractName())
                .append("&notify_url=").append(encode)
                .append("&version=").append(WechatPayConstant.CONTRACT_VERSION)
                .append("&timestamp=").append(timestamp)
                .append("&sign=").append(sign);

        return sb.toString();
    }


}
